/* eslint-disable class-methods-use-this */

import { DbColumn } from '../../models/dbColumn'
import { DbForeignKey } from '../../models/dbForeignKey'
import { DbIndex } from '../../models/dbIndex'
import { DbModel } from '../../models/dbModel'
import { DbTable } from '../../models/dbTable'

export const line: string = '\n'
export const indent: string = '    '

export abstract class DbProvider {
	private outputs = new Map<number, string>()

	public getColumnComment(column: DbColumn): string {
		let sqlComment = column.comment ? column.comment : ''
		if (sqlComment.length === 0 && column.label) {
			sqlComment = column.label
		}
		return sqlComment
	}

	public abstract getCreateTableIfNotExistsSql(model: DbModel, withComment: boolean): string
	public abstract getCreateTableSql(model: DbModel, withComment: boolean): string
	public abstract getDropTableSql(model: DbModel, withComment: boolean): string
	public abstract getDropThenCreateTableSql(model: DbModel, withComment: boolean): string
	// eslint-disable-next-line @typescript-eslint/no-unused-vars
	public getUpgradeSql(fromModel: DbModel, model: DbModel, withComment: boolean): string {
		throw new Error('Method not implemented.')
	}

	private getOutput(outputId: number): string {
		let output = this.outputs.get(outputId)
		if (output === undefined) {
			output = ''
			this.outputs.set(outputId, output)
		}

		return output
	}

	public outputCopy(): string {
		let output = ''
		let keys: number[] = []
		// eslint-disable-next-line no-restricted-syntax
		for (const key of this.outputs.keys()) {
			keys.push(key)
		}
		keys = keys.sort((a, b) => a - b)
		keys.forEach((key, index) => {
			output += (index === 0 ? '' : line) + this.outputs.get(key)
		})

		return output
	}

	public outputInitialize() {
		this.outputs = new Map<number, string>()
	}

	public outputln(text: string, outputId: number = 0) {
		let output = this.getOutput(outputId)
		output += text + line
		this.outputs.set(outputId, output)
	}

	public outputlnln(text: string, outputId: number = 0) {
		this.outputln(text + line, outputId)
	}

	public renderDefaultName(table: DbTable, column: DbColumn): string {
		return `DF_${this.renderTableName(table.name)}_${column.name}`
	}

	public renderFkName(table: DbTable, foreignKey: DbForeignKey): string {
		return this.renderFkName2(table.name, foreignKey)
	}

	public renderFkName2(tableName: string, foreignKey: DbForeignKey): string {
		return `FK_${this.renderTableName(tableName)}_${foreignKey.columns
			.replace(/[()\s]/g, '')
			.replace(/,/g, '_')}`
	}

	public renderIndexName(table: DbTable, index: DbIndex): string {
		return this.renderIndexName2(table.name, index)
	}

	public renderIndexName2(tableName: string, index: DbIndex): string {
		if (index.name) {
			return index.name
		}
		return `IK_${this.renderTableName(tableName)}_${index.columns
			.replace(/[()\s]/g, '')
			.replace(/,/g, '_')}`
	}

	public renderPkName(table: DbTable): string {
		return `PK_${this.renderTableName(table.name)}`
	}

	public renderTableName(name: string): string {
		return name.replace('\\s', '').replace('_', '')
	}

	/**
	 * make sure the length of the name is <= 64
	 * @param name the key name
	 */
	private renderName(prefix: string, columns: string, name: string): string {
		if (name.length <= 64) {
			return name
		}

		const sections: string[] = name.split('_')
		// for 1 field case, cut it
		if (sections.length <= 2) {
			return name.substring(0, 64)
		}


		return name
	}

	public stripBrackets(value: string, stripAll: boolean = false): string {
		let newValue = value
		const minLength = stripAll ? 2 : 4
		while (newValue.length > minLength) {
			if (stripAll) {
				if (newValue.startsWith('(') && newValue.endsWith(')')) {
					newValue = newValue.substring(1, newValue.length - 1)
				} else {
					break
				}
			} else if (newValue.startsWith('((') && newValue.endsWith('))')) {
				newValue = newValue.substring(2, newValue.length - 2)
			} else {
				break
			}
		}

		return newValue
	}
}
